﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
using System.Xml.Linq;
using System.Xml;

namespace udp2rs
{
    /// <summary>
    /// 传感器数据
    /// </summary>
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]//对齐方式
    public struct SensorData
    {
        public byte SynCode1;//同步码1 0xEB
        public byte SynCode2;//同步码2 0x90
        public byte Length;//长度 0x36
        public short OmegaX;//滚转角速度 分辨率0.1度/秒 范围-300~300
        public short OmegaZ;//俯仰角速度 分辨率0.1度/秒 范围-300~300
        public short OmegaY;//偏航角速度 分辨率0.1度/秒 范围-300~300
        public short Roll;//滚转角 分辨率0.1度 范围-60~60
        public short Pitch;//俯仰角 分辨率0.1度 范围-60~60
        public ushort Yaw;//航向角 分辨率0.1度 范围0~360
        public short Accex;//侧向加速度 分辨率0.01m/s2，范围-100~100
        public short Accey;//轴向加速度 分辨率0.01m/s2，范围-100~100
        public short Accez;//法向加速度 分辨率0.01m/s2，范围-100~100
        public Int32 Lon;//经度，分辨率10^-6(角度)，范围-180~180
        public Int32 Lat;//纬度，分辨率10^-6(角度)，范围-90~90
        public Int32 AltAboveSea;//海拔高度，分辨率0.25m，范围-500~12000
        public Int32 AltAtom;//气压高度，分辨率0.25m，范围-500~12000
        public UInt32 AltAboveGround;//离地高度，分辨率0.25m，范围0~12000
        public ushort AirSpeed;//空速 0.01m/s 0~360
        public short Alpha;//迎角 0.1º -60~60
        public short Beta;//侧滑角 0.1º -60~60
        public short Velo_N;//北向速度 0.01m/s 0~360
        public short Velo_E;//东向速度 0.01m/s 0~360
        public short Velo_G;//地向速度 0.01m/s 0~360
        public byte CheckCode;//校验码
    }

    /// <summary>
    /// 串口数据位列表（5,6,7,8）
    /// </summary>
    public enum SerialPortDatabits : int
    {
        FiveBits = 5,
        SixBits = 6,
        SeventBits = 7,
        EightBits = 8
    }

    /// <summary>
    /// 串口波特率列表。
    /// 75,110,150,300,600,1200,2400,4800,9600,14400,19200,28800,38400,56000,57600,
    /// 115200,128000,230400,256000
    /// </summary>
    public enum SerialPortBaudRates : int
    {
        BaudRate_75 = 75,
        BaudRate_110 = 110,
        BaudRate_150 = 150,
        BaudRate_300 = 300,
        BaudRate_600 = 600,
        BaudRate_1200 = 1200,
        BaudRate_2400 = 2400,
        BaudRate_4800 = 4800,
        BaudRate_9600 = 9600,
        BaudRate_14400 = 14400,
        BaudRate_19200 = 19200,
        BaudRate_28800 = 28800,
        BaudRate_38400 = 38400,
        BaudRate_56000 = 56000,
        BaudRate_57600 = 57600,
        BaudRate_115200 = 115200,
        BaudRate_128000 = 128000,
        BaudRate_230400 = 230400,
        BaudRate_256000 = 256000
    }
    public partial class Form1 : Form
    {
        private SerialPort ComDevice = new SerialPort();
        private byte[] data;
        private IPEndPoint ipepLocal;
        private IPEndPoint ipepTarget;
        private IPEndPoint ipepTarget2;
        bool IsUdpcRecvStart;
        private UdpClient udpcRecv;
        private UdpClient udpcSend;
        //接收并转发的线程
        Thread thrRecv;
        //显示消息线程的委托
        delegate void ShowMessageDelegate(TextBox txtbox, string message);
        FileStream fs = null;//记录文件
        public Form1()
        {
            InitializeComponent();

            //串口初始化
            comboBox_sport.Items.AddRange(SerialPort.GetPortNames());
            if (comboBox_sport.Items.Count > 0)
            {
                comboBox_sport.SelectedIndex = 0;
            }
            comboBox_baudrate.SelectedIndex = 15;//115200
            comboBox_dataBit.SelectedIndex = 3;//8
            comboBox_Parity.SelectedIndex = 0;//无校验
            comboBox_stopBit.SelectedIndex = 1;
            button_rs232.Text = "打开串口";
            label_portState.BackColor = Color.FromArgb(255, 255, 0, 0);
            label_portState.Text = "未打开";
            ComDevice.DataReceived += new SerialDataReceivedEventHandler(Com_DataReceived);//绑定事件

            //UDP和数据初始化
            //IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
            //IPAddress ipAddress = ipHostInfo.AddressList[0];
            string ipStr = "192.168.3.236";//ipAddress.ToString();
            textBox_IP.Text = ipStr;
            textBox_udpPort.Text = "8080";
            data = new byte[1024];//缓存数据
            IsUdpcRecvStart = false;
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (null != udpcRecv) udpcRecv.Close();
            if (null != udpcSend) udpcSend.Close();
            if (null != fs) fs.Close();
        }

        private void button_rs232_Click(object sender, EventArgs e)
        {
            /// <summary>
            /// 打开串口
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            if (comboBox_sport.Items.Count <= 0)
            {
                MessageBox.Show("没有发现串口,请检查线路！");
                return;
            }

            if (ComDevice.IsOpen == false)
            {
                ComDevice.PortName = comboBox_sport.SelectedItem.ToString();
                ComDevice.BaudRate = Convert.ToInt32(comboBox_baudrate.SelectedItem.ToString());
                ComDevice.Parity = (Parity)Convert.ToInt32(comboBox_Parity.SelectedIndex.ToString());
                ComDevice.DataBits = Convert.ToInt32(comboBox_dataBit.SelectedItem.ToString());
                ComDevice.StopBits = (StopBits)Convert.ToInt32(comboBox_stopBit.SelectedItem.ToString());
                try
                {
                    ComDevice.Open();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                button_rs232.Text = "关闭串口";
                label_portState.BackColor = Color.FromArgb(255, 0, 255, 0);
                label_portState.Text = "已打开";
                AddMessage(string.Format("{0}已打开", ComDevice.PortName));
            }
            else
            {
                try
                {
                    ComDevice.Close();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                button_rs232.Text = "打开串口";
                label_portState.BackColor = Color.FromArgb(255, 255, 0, 0);
                label_portState.Text = "未打开";
                AddMessage(string.Format("{0}已关闭", ComDevice.PortName));
            }

            comboBox_sport.Enabled = !ComDevice.IsOpen;
            comboBox_baudrate.Enabled = !ComDevice.IsOpen;
            comboBox_Parity.Enabled = !ComDevice.IsOpen;
            comboBox_dataBit.Enabled = !ComDevice.IsOpen;
            comboBox_stopBit.Enabled = !ComDevice.IsOpen;
        }

        private void button_resend_Click(object sender, EventArgs e)
        {
            if (!IsUdpcRecvStart) // 未监听的情况，开始监听
            {

                if (!ComDevice.IsOpen)
                {
                    MessageBox.Show("串口未打开,请检查!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                string strIP = textBox_IP.Text;
                string strTgtIP = textBox_tgtIP.Text;
                string strTgtIP2 = textBox_IP2.Text;

                IPAddress ip = IPAddress.Parse(strIP);
                IPAddress ipTgt = IPAddress.Parse(strTgtIP);
                IPAddress ipTgt2 = IPAddress.Parse(strTgtIP2);

                int iPort = Convert.ToInt32(textBox_udpPort.Text);
                int iTgtPort = Convert.ToInt32(textBox_tgtPort.Text);
                int iTgtPort2 = Convert.ToInt32(textBox_port2.Text);

                try
                {
                    ipepLocal = new IPEndPoint(ip, iPort);

                    udpcRecv = new UdpClient(ipepLocal);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "本地UDP初始化错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                try
                {
                    ipepTarget = new IPEndPoint(ipTgt, iTgtPort);
                    ipepTarget2 = new IPEndPoint(ipTgt2, iTgtPort2);
                    udpcSend = new UdpClient();
                    udpcSend.Connect(ipepTarget);

                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "目标UDP初始化错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                thrRecv = new Thread(ReceiveMessageAndResend);
                thrRecv.Start();
                button_rs232.Enabled = false;//在监听期不能关闭串口
                IsUdpcRecvStart = true;
                button_resend.Text = "停止监听和转发";
                AddMessage(string.Format("已开始监听"));
                timer_ShowInfo.Enabled = true;
                timer_SendDataUDPPort.Enabled = true;
                fs = new FileStream(Environment.CurrentDirectory + "\\logfile" + DateTime.Now.ToString("HHmmss") + ".txt", FileMode.Create);
            }
            else                  // 正在监听的情况，终止监听
            {
                thrRecv.Abort(); // 必须先关闭这个线程，否则会异常
                udpcRecv.Close();
                udpcSend.Close();
                button_resend.Text = "开始监听并转发";
                button_rs232.Enabled = true;//结束监听后可以关闭串口
                IsUdpcRecvStart = false;
                AddMessage(string.Format("已停止监听"));
                timer_ShowInfo.Enabled = false;
                timer_SendDataUDPPort.Enabled = false;
                if (fs != null)
                {
                    fs.Flush();
                    fs.Close();
                    fs = null;
                }
            }
        }

        private void button_clear_Click(object sender, EventArgs e)
        {
            textBox_info.Items.Clear();
        }

        /// <summary>
        /// 接收数据并转发
        /// </summary>
        /// <param name="obj"></param>
        private void ReceiveMessageAndResend(object obj)
        {
            IPEndPoint remoteIpep = new IPEndPoint(IPAddress.Any, 0);

            while (true)
            {
                try//先检查udp口是否接收到数据，并转发
                {
                    byte[] bytRecv = udpcRecv.Receive(ref remoteIpep);
                    ParseUDPSensor(bytRecv);
                    SendDataSerialPort(bytRecv);
                    string message = "";
                    foreach (byte b in bytRecv)
                    {
                        message = message + Convert.ToString(b, 16) + " ";
                    }
                    //string message = Encoding.ASCII.GetString(
                    //    bytRecv, 0, bytRecv.Length);
                    ShowMessage(textBox_info, string.Format("{0}->[{1}]->{2}\r\n", remoteIpep, message, ComDevice.PortName));
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "UDP接收错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    break;
                }
            }
        }

        Queue<SensorData> m_QueueSensor = new Queue<SensorData>();
        public const int LENGTH_SENSORDATA = 0x36;//传感器数据包长度
        private void ParseUDPSensor(byte[] bytRecv)
        {
            if (bytRecv.Length < LENGTH_SENSORDATA)
                return;
            if (bytRecv[0] != 0xEB || bytRecv[1] != 0x90 || bytRecv[2] != (byte)LENGTH_SENSORDATA)
                return;
            SensorData m_SensorData = (SensorData)Program.ByteToStruct(bytRecv, typeof(SensorData));
            m_QueueSensor.Enqueue(m_SensorData);
        }


        /// <summary>
        /// 串口发送数据
        /// </summary>
        /// <param name="data"></param>
        public bool SendDataSerialPort(byte[] data)
        {
            if (ComDevice.IsOpen)
            {
                try
                {
                    ComDevice.Write(data, 0, data.Length);//发送数据
                    return true;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "串口打开错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            else
            {
                MessageBox.Show("串口未打开", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            return false;
        }

        Queue<byte[]> m_QueueSerialData = new Queue<byte[]>();
        /// <summary>
        /// 串口接收数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Com_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (IsUdpcRecvStart)
            {
                byte[] ReDatas = new byte[ComDevice.BytesToRead];
                try
                {
                    ComDevice.Read(ReDatas, 0, ReDatas.Length);
                    m_QueueSerialData.Enqueue(ReDatas);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "串口接收错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }
        }


        /// <summary>
        /// UDP网口发送数据
        /// </summary>
        /// <param name="data"></param>
        public void SendDataUDPPort(byte[] ReDatas)
        {
            if (null == ReDatas)
                return;
            try
            {
                udpcSend.Connect(ipepTarget);
                udpcSend.Send(ReDatas, ReDatas.Length);
                udpcSend.Connect(ipepTarget2);
                udpcSend.Send(ReDatas, ReDatas.Length);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "UDP发送错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            string message = "";
            foreach (byte b in ReDatas)
            {
                message = message + Convert.ToString(b, 16) + " ";
            }
            //string message = Encoding.ASCII.GetString(ReDatas, 0, ReDatas.Length);
            ShowMessage(textBox_info, string.Format("{0}->[{1}]->{2}\r\n", ComDevice.PortName, message, ipepTarget));
        }

        Queue<string> m_QueueMessage = new Queue<string>();

        private void ShowMessage(ListBox txtbox, string message)
        {
            //if (null != message)
            //    m_QueueMessage.Enqueue(message);
            /*
            if (txtbox.InvokeRequired)
            {
                ShowMessageDelegate showMessageDelegate = ShowMessage;
                txtbox.Invoke(showMessageDelegate, new object[] { txtbox, message });
            }
            else
            {
                txtbox.Text += message + "\r\n";
            }
            */
        }

        private void timer_ShowInfo_Tick(object sender, EventArgs e)
        {
            while (m_QueueMessage.Count > 0)
            {
                string message = m_QueueMessage.Dequeue();
                AddMessage(message);
            }
            if (m_QueueSensor.Count > 0)
            {
                /*
                SensorData data = m_QueueSensor.Dequeue();
                string sensordata = "phi:" + data.Roll.ToString() + " theta:" + data.Pitch.ToString() + " yaw:" + data.Yaw.ToString();
                textBox_info.Items.Insert(0, sensordata);
                m_QueueSensor.Clear();
                */
            }
        }

        private void timer_SendDataUDPPort_Tick(object sender, EventArgs e)
        {
            bool bsend = false;
            while (m_QueueSerialData.Count > 0)
            {
                byte[] ReDatas = m_QueueSerialData.Dequeue();
                if (bsend == false)
                {
                    SendDataUDPPort(ReDatas);
                    bsend = true;
                }
            }
        }

        private void AddMessage(string msg)
        {
            if (null == msg)
                return;
            msg = DateTime.Now.ToString("HH:mm:ss >> ") + msg;
            //if (textBox_info.InvokeRequired)
            //{
            //    ShowMessageDelegate showMessageDelegate = ShowMessage;
            //    textBox_info.Invoke(showMessageDelegate, new object[] { textBox_info, message });
            //}
            //else
            //{
            //textBox_info.Items.Insert(0, msg);
            //}
            /*
            if (fs != null)
            {
                byte[] bytes = Encoding.UTF8.GetBytes(msg);
                fs.Write(bytes, 0, bytes.Length);
            }
            */
        }

        private void textBox_info_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        private void 保存配置ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //string localFilePath, fileNameExt, newFileName, FilePath; 
            SaveFileDialog sfd = new SaveFileDialog();
            //设置文件类型 
            sfd.Filter = "配置文件（*.xml）|*.xml";

            //设置默认文件类型显示顺序 
            sfd.FilterIndex = 1;

            //保存对话框是否记忆上次打开的目录 
            sfd.RestoreDirectory = true;

            //点了保存按钮进入 
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                string localFilePath = sfd.FileName.ToString(); //获得文件路径 

                if(!ComDevice.IsOpen)
                {
                    ComDevice.PortName = comboBox_sport.SelectedItem.ToString();
                    ComDevice.BaudRate = Convert.ToInt32(comboBox_baudrate.SelectedItem.ToString());
                    ComDevice.Parity = (Parity)Convert.ToInt32(comboBox_Parity.SelectedIndex.ToString());
                    ComDevice.DataBits = Convert.ToInt32(comboBox_dataBit.SelectedItem.ToString());
                    ComDevice.StopBits = (StopBits)Convert.ToInt32(comboBox_stopBit.SelectedItem.ToString());
                }

                
                string strIP = textBox_IP.Text;
                string strTgtIP = textBox_tgtIP.Text;
                string strTgtIP2 = textBox_IP2.Text;

                IPAddress ip = IPAddress.Parse(strIP);
                IPAddress ipTgt = IPAddress.Parse(strTgtIP);
                IPAddress ipTgt2 = IPAddress.Parse(strTgtIP2);

                int iPort = Convert.ToInt32(textBox_udpPort.Text);
                int iTgtPort = Convert.ToInt32(textBox_tgtPort.Text);
                int iTgtPort2 = Convert.ToInt32(textBox_port2.Text);

                ipepLocal = new IPEndPoint(ip,iPort);
                ipepTarget = new IPEndPoint(ipTgt,iTgtPort);
                ipepTarget2 = new IPEndPoint(ipTgt2,iTgtPort2);

                XElement xElement = new XElement("通信设置",
                                        new XElement("UDP设置",
                                            new XElement("本地UDP设置",
                                                new XElement("IP", ipepLocal.Address.ToString()),
                                                new XElement("端口", ipepLocal.Port.ToString())
                                                ),
                                            new XElement("目标UDP1",
                                                new XElement("IP", ipepTarget.Address.ToString()),
                                                new XElement("端口", ipepTarget.Port.ToString())
                                                ),
                                            new XElement("目标UDP2",
                                                new XElement("IP", ipepTarget2.Address.ToString()),
                                                new XElement("端口", ipepTarget2.Port.ToString())
                                                )
                                           ),
                                        new XElement("串口设置",
                                                new XElement("端口", ComDevice.PortName),
                                                new XElement("波特率", ComDevice.BaudRate),
                                                new XElement("数据位", ComDevice.DataBits),
                                                new XElement("停止位", ComDevice.StopBits),
                                                new XElement("校验位", ComDevice.Parity)
                                            )
                                        );

                //需要指定编码格式，否则在读取时会抛：根级别上的数据无效。 第 1 行 位置 1异常
                XmlWriterSettings settings = new XmlWriterSettings();
                settings.Encoding = new UTF8Encoding(false);
                settings.Indent = true;
                XmlWriter xw;
                try
                {
                    xw = XmlWriter.Create(localFilePath, settings);
                    //xw.WriteElementString(xElement);
                    //写入文件
                    xElement.WriteTo(xw);
                    xw.Flush();
                    xw.Close();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "保存配置错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

            }
        }

        private void saveFileDialog_saveConfig_FileOk(object sender, CancelEventArgs e)
        {
        }
            
    }
}
